/*
 * Copyright © 2017 eqxiu.com 北京中网易企秀科技有限公司  All rights reserved.
 */
package cn.hermit.data.json.handler;

import cn.hermit.data.json.factory.OrderedJSONFactory;
import cn.hermit.data.json.model.JSONArray;
import cn.hermit.data.json.model.JSONDocument;
import cn.hermit.data.json.model.JSONFactory;
import cn.hermit.data.json.model.JSONObject;
import cn.hermit.data.json.model.JSONValue;
import cn.hermit.core.FastStack;

/**
 * Using steaming API to create JSON Document.
 * 
 * <p>
 * In <a href="http://json.org" target="_blank">JSON specification</a>, 
 * there is no items about JSON order and JSON duplicated Keys.
 * But some cases requires to keep order of JSON keys or to keep duplicated keys.
 * The implementations of JSONFactory provides many factories to create various kinds of JSON Object, 
 * which allow duplicated keys or keep keys order.
 * Please see the abstract class {@link JSONFactory JSONFactory}
 * and the classes in package of {@link com.eqxiu.tiny.data.json.factory various factories }
 * </p>
 * 
 * @author Jack Gao (Chinese name : GAO JIANGUO, Email : linux.gjg@gmail.com)
 * @date 18 Jun, 2014
 */
public class JSONHandler implements ContentHandler {

	/**
	 * JSON factory to create JSON objects
	 */
	protected JSONFactory jsonFactory;

	private FastStack<JSONDocument> stack;

	private String key;

	/**
	 * Constructor with a default ordered JSONFactory.
	 * 
	 */
	public JSONHandler() {
		this(new OrderedJSONFactory());
	}

	/**
	 * Constructor with a JSONFactory as parameter.
	 * 
	 * @param jsonFactory
	 * 		JSON factory to create JSON objects
	 */
	public JSONHandler(JSONFactory jsonFactory) {
		if (jsonFactory == null) {
			jsonFactory = new OrderedJSONFactory();
		}
		this.jsonFactory = jsonFactory;
		stack = new FastStack<JSONDocument>();
	}
	
	@Override
	public void startDocument() {
		
	}

	@Override
	public void startObject(boolean inArray) {
		JSONDocument parent = stack.peek();
		JSONObject jo = this.jsonFactory.createObject();
		if (JSONArray.class.isInstance(parent)) {
			this.jsonFactory.arrayValue(((JSONArray) parent), jo);
		} else if (JSONObject.class.isInstance(parent) && key != null) {
			this.jsonFactory.objectEntry((JSONObject) parent, key, jo);
			key = null;
		}
		stack.push(jo);
	}

	@Override
	public void key(String key) {
		this.key = key;
	}

	@Override
	public void startArray(boolean inArray) {
		JSONDocument parent = stack.peek();
		JSONArray ja = this.jsonFactory.createArray();
		if (JSONArray.class.isInstance(parent)) {
			this.jsonFactory.arrayValue(((JSONArray) parent), ja);
		} else if (JSONObject.class.isInstance(parent) && key != null) {
			this.jsonFactory.objectEntry((JSONObject) parent, key, ja);
			key = null;
		}
		stack.push(ja);
	}

	@Override
	public void value(String value, boolean isText,boolean inArray) {
		JSONDocument parent = stack.peek();
		JSONValue jv = this.jsonFactory.createValue(value, isText);
		if (JSONArray.class.isInstance(parent)) {
			this.jsonFactory.arrayValue(((JSONArray) parent), jv);
		} else if (JSONObject.class.isInstance(parent) && key != null) {
			this.jsonFactory.objectEntry((JSONObject) parent, key, jv);
			key = null;
		}
	}

	@Override
	public void endArray(boolean inArray) {
		if (stack.size() > 1)
			stack.pop();
	}

	@Override
	public void endObject(boolean inArray) {
		if (stack.size() > 1)
			stack.pop();
	}
	
	@Override
	public void endDocument() {
		
	}

	/**
	 * @return
	 * 		JSON Document after parsing
	 */
	public JSONDocument getDocument() {
		return stack.peek();
	}

	/**
	 * @return
	 * 		JSON factory to create JSON objects
	 */
	public JSONFactory getJSONFactory() {
		return this.jsonFactory;
	}

}